/* Universidad del Valle de Guatemala
 * 
 * Consiste en un parser de posfix, evaluando cada una de las expresiones
 * dadas.
 * 
 *  Autor: Jorge Luis Martínez
 *  Fecha: 16/jul/2012
 */

package Interprete;
import Stack.*;
import java.text.ParseException;

/**
 * Evalua las expresiones dadas en posfix.
 * @author Jorge Luis Martínez
 */
public class InterpretePosfix {
    private Stack<String> pilaDatos;
    
    /**
     * Inicializa el parser.
     * @Pre
     * @Post
     */
    public InterpretePosfix(){
        pilaDatos = new StackVector<>();
    }
    
    /**
     * Trata de evaluar el string dado como una expresión en formato posfix.
     * @param expresion String con la expresión en posfix
     * @return Resultado de la operación.
     * @throws ParseException La excepción es lanzada si existen errores
     * durante el proceso de interpretación.
     * @Pre 
     * @Post 
     */
    public double evaluar( String expresion ) throws ParseException{
        // Vaciar la pila
        limpiarPila();
        
        // Separar el string por espacios.
        String[] partes = expresion.split(" ");
        
        for (String operando : partes) {
            // Revisar si es un operando u operador
            try {
                double valor = Double.parseDouble(operando);
                pilaDatos.push(Double.toString(valor));
            } catch (NumberFormatException numberFormatException) {
                // Si es espacio se ignora
                if( operando.isEmpty() )
                    continue;
                // Es un operador
                pilaDatos.push( operando );
                operar();
            }
        }
        
        // Solo debe de quedar un valor en la pila, el cual es el resultado
        if( pilaDatos.size() != 1 )
            throw new ParseException( "Exceso de argumentos", pilaDatos.size() );
        
        double resultado = Double.parseDouble( pilaDatos.pop() );
        return resultado;
    }
    
    /**
     * Elimina todos los datos en la pila.
     * @Pre 
     * @Post 
     */
    protected void limpiarPila(){
        while( !pilaDatos.empty() ){
            pilaDatos.pop();
        }
    }
    
    /**
     * Opera el primer operador binario que se encuentra en la pila.
     * @throws ParseException Si no hay suficientes operandos.
     * 
     * @Pre  En la pila deben de estar el operador y dos operandos.
     * @Post  Los tres valores se eliminan de la pila y son reemplazados
     * por el resultado de la operación.
     */
    protected void operar() throws ParseException{
        /* Validar */
        if( pilaDatos.size() < 3 )
            throw new ParseException( "Insuficiente cantidad de argumentos para operación.", pilaDatos.size() );
        
        // Obtener operador
        String operador = pilaDatos.pop();
        
        // Obtener operandos (los cuales están invertidos en la pila)
        double op1 = 0.0, op2 = 0.0;
        try {
            op2 = Double.parseDouble(pilaDatos.pop());
            op1 = Double.parseDouble(pilaDatos.pop());
        } catch (NumberFormatException numberFormatException) {
            throw new ParseException( "Operandos inválidos", pilaDatos.size() );
        }
        
        // Realizar la operación
        double resultado = 0;
        switch (operador) {
            case "+":
                resultado = op1 + op2;
                //System.out.println( "Operacion: " + op1 + "+" + op2 );
                break;
            case "-":
                resultado = op1 - op2;
                //System.out.println( "Operacion: " + op1 + "-" + op2 );
                break;
            case "*":
                resultado = op1 * op2;
                //System.out.println( "Operacion: " + op1 + "*" + op2 );
                break;
            case "/":
                if( op2 == 0.0 )
                    throw new ParseException( "División por cero", pilaDatos.size() );
                resultado = op1 / op2;
                //System.out.println( "Operacion: " + op1 + "/" + op2 );
                break;
            default:
                throw new ParseException( "Operación no soportada - " + operador,
                        pilaDatos.size() );
        }
        
        // Empujar el resultado a la pila
        pilaDatos.push( Double.toString(resultado) );
    }
}
